跳到主要内容

结构型模式-代理模式

代理模式

classDiagram Client --> Subject Subject <|.. RealSubject Subject <|.. Proxy Proxy --> RealSubject Subject: <<interface>> Subject: +request() RealSubject: +request() Proxy: -RealSubject realSubject Proxy: +Proxy(RealSubject) Proxy: +request()

静态代理

public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("这是被代理对象执行的方法");
}
}
public class Proxy implements Subject {
private RealSubject realSubject;

public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}

@Override
public void request() {
System.out.println("这是代理执行的方法");
realSubject.request();
}
}
// demo
public static void main(String[] args) {
Proxy proxy = new Proxy(new RealSubject());
proxy.request();
}

动态代理

这里只介绍 JDK 自带的代理工具类

首先了解如下几个类:

Proxy

专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。

提供了许多用于创建动态代理类和动态代理对象的静态方法。

// 关键就是这个 newProxyInstance 方法
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

这里需要传入一个 InvocationHandler 对象

InvocationHandler

InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。

InvocationHandler 内部只有一个 invoke() 方法,正是这个方法决定了怎么样处理代理传递过来的方法调用。

  • proxy:当前代理实例,一般不用它,因为调用了它的方法,会在这里被反射调用,导致递归从而死循环
  • method:代理对象调用的方法
  • args:调用的方法中的参数

因为,Proxy 动态产生的代理对象会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。

使用例

故而,总的运行流程为:获取代理实例 -–> 代理实例.方法 --> InvocationHandler.invoke() --> 接口真正实现类的方法

public class Temp {
public static void main(String[] args) {
Human human = new Everyman();
human.info("张三");
human.walk();

System.out.println("======================================================");

// 生成代理对象
Human proxyInstance = (Human)Proxy.newProxyInstance(
human.getClass().getClassLoader(),
human.getClass().getInterfaces(),
(Object proxy, Method method, Object[] parameter) -> {

if ("info".equals(method.getName())) {
// 这里直接调用了上面创建的实例对象,一般来说是创建一个 InvocationHandler 实现类,通过构造方法传进来的
method.invoke(human, parameter);
System.out.println("现在我被代理了,所以现在我是代理人!我的名字是:" + parameter[0]);
}

if ("walk".equals(method.getName())) {
method.invoke(human, parameter);
System.out.println("现在我被代理了,所以现在我能跑 200米不带喘!");
}

return proxy;
});

proxyInstance.info("李四");
proxyInstance.walk();
}

// 还是需要先定义一个接口
interface Human {
void info(String name);
void walk();
}

static class Everyman implements Human{
@Override
public void info(String name) {
System.out.println("我是个普通人,我的名字是:" + name);
}

@Override
public void walk() {
System.out.println("我能跑 50米不带喘");
}
}
}

输出为:

我是个普通人,我的名字是:张三
我能跑 50米不带喘
======================================================
我是个普通人,我的名字是:李四
现在我被代理了,所以现在我是代理人!我的名字是:李四
我能跑 50米不带喘
现在我被代理了,所以现在我能跑 200米不带喘!

一般是单独实现 InvocationHandler 这个接口,这样可以使用构造方法,把被代理对象传进来,而不是上面例子那样直接把实例对象丢到匿名内部类里面执行